package com.itheima.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.stock.common.domain.*;
import com.itheima.stock.config.StockInfoConfig;
import com.itheima.stock.mapper.*;
import com.itheima.stock.pojo.StockBusiness;
import com.itheima.stock.service.StockService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.utils.IdWorker;
import com.itheima.stock.vo.resp.PageResult;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service("stockService")
public class StockServiceImpl implements StockService {

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private StockOuterMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;


    @Autowired
    private IdWorker idWorker;

    /**
     * 注入httpClinet bean，调用远程股票api接口
     */
    @Autowired
    private RestTemplate restTemplate;

    @Override
    public List<StockBusiness> getAllStockBusiness() {
        return stockBusinessMapper.getAll();
    }

    /**
     * 获取最新国内A股大盘信息（上证+深证的大盘信息）
     *
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> getInnerMarketInfos() {
        //1.获取最近最新的一次股票有效交易时间点（精确分钟）
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //因为对于当前来说，我们没有实现股票信息实时采集的功能，所以最新时间点下的数据
        //在数据库中是没有的，所以，先临时指定一个假数据,后续注释掉该代码即可
        curDate = DateTime.parse("2021-12-28 09:31:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.获取国内大盘的code信息
        List<String> innerCodes = stockInfoConfig.getInner();
        //3.调用mapper查询 关注点分离
        List<InnerMarketDomain> infos = stockMarketIndexInfoMapper.getInnerMarketInfosByCodesAndTime(innerCodes, curDate);
        //4.返回数据
        return R.ok(infos);
    }

    /**
     * 查询最新板块数据，按照交易金额降序取前10
     *
     * @return
     */
    @Override
    public R<List<StockBlockDomain>> getStockBlockInfos() {
        //1.获取最近最新的一次股票有效交易时间点（精确分钟）
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //因为对于当前来说，我们没有实现股票信息实时采集的功能，所以最新时间点下的数据
        //在数据库中是没有的，所以，先临时指定一个假数据,后续注释掉该代码即可
        curDate = DateTime.parse("2022-01-14 16:57:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.获取板块数据
        int order = 10;
        List<StockBlockDomain> infos = stockBlockRtInfoMapper.getStockBlockInfos(curDate, order);
        return R.ok(infos);
    }

    /**
     * day03------分页查询涨幅前十
     *
     * @return
     */
    @Override
    public R<List<StockUpdownDomain>> getStockIncreaseLimit() {
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        curDate = DateTime.parse("2022-03-21 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        // PageHelper.startPage(1, 4);
        List<StockUpdownDomain> infos = stockRtInfoMapper.getAllStockUpDownByTime(curDate);
        if (CollectionUtils.isEmpty(infos)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(infos);
    }


    /**
     * 沪深两市个股行情列表查询 ,以时间顺序和涨幅分页查询
     *
     * @param page     当前页
     * @param pageSize 每页大小
     * @return
     */
    @Override
    public R<PageResult<StockUpdownDomain>> stockPage(Integer page, Integer pageSize) {
        PageHelper.startPage(page, pageSize);
        List<StockUpdownDomain> infos = stockRtInfoMapper.stockAll();
        if (CollectionUtils.isEmpty(infos)) {
            return R.error("暂无数据");
        }
        PageInfo<StockUpdownDomain> listPageInfo = new PageInfo<>(infos);
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(listPageInfo);
        return R.ok(pageResult);
    }

    @Override
    public R<Map> upDownCount() {
        //1.借助工具类获取最近交易日的开盘时间和收盘时间
        //获取有效时间点
        DateTime avableTimePoint = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //根据有效的时间点获取对应日期的开盘和收盘日期
        Date openTime = DateTimeUtil.getOpenDate(avableTimePoint).toDate();
        Date closeTime = DateTimeUtil.getCloseDate(avableTimePoint).toDate();
        //TODO mock数据
        openTime = DateTime.parse("2021-12-19 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        closeTime = DateTime.parse("2021-12-19 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.查询涨停的统计数据
        List<Map> upList = stockRtInfoMapper.getStockUpDownCount(openTime, closeTime, 1);
        //3.查询跌停的统计数据
        List<Map> downList = stockRtInfoMapper.getStockUpDownCount(openTime, closeTime, 0);
        //4.组装map：将涨停和跌停的数据组装到map中
        HashMap<String, List> map = new HashMap<>();
        map.put("upList", upList);
        map.put("downList", downList);
        //5.返回结果
        return R.ok(map);
    }

    /**
     * 通过easyExcel导出指定页的股票涨幅信息
     *
     * @param response 通过响应对象获取输出流，完成excel文件流对象的输入
     * @param page     当前页
     * @param pageSize 每页大小
     */
    @Override
    public void exportStockInfo(HttpServletResponse response, Integer page, Integer pageSize) {
        //1.设置响应数据的类型:excel
        try {
            response.setContentType("application/vnd.ms-excel");
            //2.设置响应数据的编码格式
            response.setCharacterEncoding("utf-8");
            //3.设置默认的文件名称
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("stockRt", "UTF-8");
            //设置默认文件名称：兼容一些特殊浏览器
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //1.获取最近最新的一次股票有效交易时间点（精确分钟）
            Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //因为对于当前来说，我们没有实现股票信息实时采集的功能，所以最新时间点下的数据
            //在数据库中是没有的，所以，先临时指定一个假数据,后续注释掉该代码即可
            curDate = DateTime.parse("2022-05-13 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //2.设置分页参数 底层会拦截mybatis发送的sql，并动态追加limit语句实现分页
            PageHelper.startPage(page, pageSize);
            //3.查询
            List<StockUpdownDomain> infos = stockRtInfoMapper.getAllStockUpDownByTime(curDate);
            //4.响应excel流
            EasyExcel
                    .write(response.getOutputStream(), StockUpdownDomain.class)
                    .sheet("股票信息")
                    .doWrite(infos);
        } catch (IOException e) {
            e.printStackTrace();
            //log.info("当前导出数据异常，当前页：{},每页大小：{},异常信息：{}",page,pageSize,e.getMessage());
        }
    }

    /**
     * 功能描述：统计国内A股大盘T日和T-1日成交量对比功能（成交量为沪市和深市成交量之和）
     * map结构示例：
     * {
     * "volList": [{"count": 3926392,"time": "202112310930"},......],
     * "yesVolList":[{"count": 3926392,"time": "202112310930"},......]
     * }
     *
     * @return
     */
    @Override
    public R<Map> stockTradeVol4InnerMarket() {
        //1.获取T日交易时间范围
        //1.1 获取最新交易时间点即可
        DateTime tEndDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date tEndDate = tEndDateTime.toDate();
        Date tStartDate = DateTimeUtil.getOpenDate(tEndDateTime).toDate();
        //mock data
        tEndDate = DateTime.parse("2022-01-03 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        tStartDate = DateTime.parse("2022-01-03 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.根据T日获取T-1日的交易时间范围
        //2.1 获取T日的上一个有效交易日，就是T-1日
        DateTime preTEndDateTime = DateTimeUtil.getPreviousTradingDay(tEndDateTime);
        Date preTEndDate = preTEndDateTime.toDate();
        Date preTStartDate = DateTimeUtil.getOpenDate(preTEndDateTime).toDate();
        preTEndDate = DateTime.parse("2022-01-02 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        preTStartDate = DateTime.parse("2022-01-02 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //3.获取大盘code编码集合
        List<String> innerCodes = stockInfoConfig.getInner();
        //4.分步查询t日和t-1日的交易数据，组装
        //查询T日数据
        List<Map> tData = stockMarketIndexInfoMapper.getAmountInfoByRangeTime(tStartDate, tEndDate, innerCodes);
        //查询T-1日数据
        List<Map> preTData = stockMarketIndexInfoMapper.getAmountInfoByRangeTime(preTStartDate, preTEndDate, innerCodes);
        //组装数据
        HashMap<String, List> mapInfo = new HashMap<>();
        mapInfo.put("volList", tData);
        mapInfo.put("yesVolList", preTData);
        //5.返回指定格式的数据
        return R.ok(mapInfo);
    }

    /**
     * 分时K线
     * @return
     */
    @Override
    public R<Map> getStockUpDownSection() {
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //curDate = DateTime.parse("2021-12-30 09:36:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        List<Map> maps = stockRtInfoMapper.getStockUpDownSectionByTime(curDate);
        List<String> orderSections = stockInfoConfig.getUpDownRange();
        List<Map> orderMaps = orderSections.stream().map(title -> {
            Map mp = null;
            Optional<Map> op = maps.stream().filter(m -> m.containsValue(title)).findFirst();
            //判断是否存在符合过滤条件的元素
            if (op.isPresent()) {
                mp = op.get();
            } else {
                mp = new HashMap();
                mp.put("count", 0);
                mp.put("title", title);
            }
            return mp;
        }).collect(Collectors.toList());
        HashMap<String, Object> mapInfo = new HashMap<>();
        //获取指定日期格式的字符串
        String curDateStr = new DateTime(curDate).toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        mapInfo.put("time", curDateStr);
        mapInfo.put("infos", orderMaps);
        return R.ok(mapInfo);
    }


    /**
     * 查询股票的日K线数据
     *
     * @param stockCode 股票的编码
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> getStockScreenTimeSharing(String stockCode) {
        //1.获取最新股票交易时间点和开盘时间点
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date startDate = DateTimeUtil.getOpenDate(endDateTime).toDate();
        Date endDate = endDateTime.toDate();
        //mock data
        endDate = DateTime.parse("2022-06-10 14:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        startDate = DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.查询指定日期范围内的数据
        List<Stock4MinuteDomain> infos = this.stockRtInfoMapper.getStockSecondInfoByCodeAndTime(startDate, endDate, stockCode);
        //3.响应
        return R.ok(infos);
    }

    @Override
    public R<List<Stock4EvrDayDomain>> getStockScreenDkLine(String stockCode) {
        //1.获取日K线查询的时间范围
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endDate = endDateTime.toDate();
        Date startDate = endDateTime.minusDays(200).toDate();
        //mock data
       // endDate = DateTime.parse("2022-06-10 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
       // startDate = DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.调用mapper查询
        List<Stock4EvrDayDomain> infos = this.stockRtInfoMapper.getStockEvyDayInfoByCodeAndTime(startDate, endDate, stockCode);
        return R.ok(infos);
    }

    /**
     * 采集外盘指数
     * @return
     */
    @Override
    public R<List<externalMarketDomain>> getExternalMarketInfos() {
        //1.定义采集的url接口
        String url = stockInfoConfig.getMarketUrl() + String.join(",", stockInfoConfig.getOuter());
        //2.调用restTemplate采集数据
        //2.1 组装请求头
        HttpHeaders headers = new HttpHeaders();
        //必须填写，否则数据采集不到
        headers.add("Referer", "https://finance.sina.com.cn/stock/");
        headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
        //2.2 组装请求对象
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        //2.3 resetTemplate发起请求
        String resString = restTemplate.postForObject(url, entity, String.class);
        String reg = "var hq_str_(.+)=\"(.+)\";";
        //编译表达式,获取编译对象
        Pattern pattern = Pattern.compile(reg);
        //匹配字符串
        Matcher matcher = pattern.matcher(resString);
        List<externalMarketDomain> list = new ArrayList<>();
        //判断是否有匹配的数值
        while (matcher.find()) {
            //获取大盘的code
            String marketCode = matcher.group(1);
            //获取其它信息，字符串以逗号间隔
            String otherInfo = matcher.group(2);
            //以逗号切割字符串，形成数组
            String[] splitArr = otherInfo.split(",");
            //大盘名称
            String marketName = splitArr[0];
            //获取大盘的当前点数
            BigDecimal curPoint = new BigDecimal(splitArr[1]);
            //涨跌值
            BigDecimal upDown = new BigDecimal(splitArr[2]);
            //涨幅
            BigDecimal rose = new BigDecimal(splitArr[3]);

            //时间
            Date curTime = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //3、组装entity对象
            externalMarketDomain info = externalMarketDomain.builder().name(marketName)
                    .curPoint(curPoint)
                    .upDown(upDown)
                    .rose(rose)
                    .curTime(curTime).build();
            list.add(info);

            list.sort((a,b)->b.getRose().compareTo(a.getRose()));

        }

        List<externalMarketDomain> newList = list.subList(0, 4);
        //4.返回数据
        return R.ok(newList);


    }

    /**
     * 模糊查询
     * @param stockCode
     * @return
     */
    @Override
    public R<List<StockSearchDomain>> searchStockByNum(String stockCode) {
        //校验
        boolean result = stockCode.matches("[0-9]+");
        if (!result){
            return R.error("请输入数字");
        }
        stockCode="%"+stockCode+"%";
        // WOBAIDUXIA
        List<StockSearchDomain> infos= this.stockRtInfoMapper.searchStockByNum(stockCode);

        return R.ok(infos);
    }

    @Override
    public R<StockDescriptionDomain> searchDescriptionByCode(String stcckCode) {
      StockDescriptionDomain info=  this.stockBusinessMapper.searchDescriptionByCode(stcckCode);
        return R.ok(info);
    }
    /**
     * day07-------查询股票的周K线数据
     * @param stockCode 股票的编码
     * @return
     */
    @Override
    public R<List<Stock4EvrWeekDomain>> getStockScreenWkLine(String stockCode) {
//        //1.添加股票前缀 sh sz
//        stockCode = stockCode.startsWith("6") ? "sh" + stockCode : "sz" + stockCode;
//        //2.1设置请求头的请求参数类型
//        HttpHeaders headers = new HttpHeaders();
//        headers.add("Referer","https://finance.sina.com.cn/stock/");
//        headers.add("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36");
//        //2.2 组装请求对象
//        HttpEntity<Object> entity = new HttpEntity<>(headers);
//        //3.组装请求地址
//        String url=stockInfoConfig.getMarketUrl()+String.join(",",stockCode);
//        //4.restTemplate发起请求
//        String result = restTemplate.postForObject(url, entity, String.class);
//        //定义正则 第一组：匹配任意非空字符串  第二组：匹配任意除了换行符的字符串包括名称中出现空格的数据
//        String reg="var hq_str_(.+)=\"(.+)\";";
//        //编译正则，获取正则对象
//        Pattern pattern = Pattern.compile(reg);
//        //获取正则匹配器
//        Matcher matcher = pattern.matcher(stockCode);
//        stockCode=matcher.group(1);
//        ////去除股票sz或者sh前缀 shxxxx
//        stockCode = stockCode.substring(2);
//        String other=matcher.group(2);
//        String[] others=other.split(",");
//        //大盘名称
//        String stockName = others[0];
//        //今日开盘价
//        BigDecimal openPrice = new BigDecimal(others[1]);
//        //昨日收盘价
//        BigDecimal preClosePrice = new BigDecimal(others[2]);
//        //当前价格
//        BigDecimal currentPrice = new BigDecimal(others[3]);
//        //今日最高价额
//        BigDecimal maxPrice = new BigDecimal(others[4]);
//        //今日最低价额
//        BigDecimal minPrice = new BigDecimal(others[5]);
//        //成金额
//        BigDecimal tradeVol = new BigDecimal(others[9]);
//        //成交量
//        Long tradeAmount = Long.valueOf(others[8]);
//        //时间
//        Date curTime = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
//        //----------------------------------------
//Stock4EvrWeekDomain.builder()
//        .avgPrice()
//        .minPrice()
//        .openPrice()
//        .maxPrice()
//        .closePrice()
//        .mxTime()
//        .stockCode()
//        .build();

        //获取周K线查询的时间范围
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endDate = endDateTime.toDate();
        Date startDate = endDateTime.minusWeeks(30).toDate();
        //2.调用mapper查询
        List<Stock4EvrWeekDomain> infos= this.stockRtInfoMapper.getStockEvyWeekInfoByCodeAndTime(startDate,endDate,stockCode);
        return R.ok(infos);

    }
    /**
     * DAY08----最新分时行情数据
     */
    @Override
    public R<Stock4MinuteDomain> getStockLatestInfo(String stockCode) {

        Stock4MinuteDomain infos=this.stockRtInfoMapper.getStockLatestInfo(stockCode);

        return R.ok(infos);
    }


    /**
     *  day08----个股实时交易流水查询
     */
    @Override
    public R<StockLastestAmtDomain> getStockLastestAmt(String stockCode) {
        StockLastestAmtDomain infos=this.stockRtInfoMapper.getStockLastestAmt(stockCode);
        return  R.ok(infos);
    }


}


